using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.CodeDom.Compiler;

using NerdBank.HtmlScriptingClient;

namespace Lds.Ward.PhotoDirectory.Logic {
	public class FamiliesToWebSite {
		const string loginUrl = "https://secure.lds.org/units/login/0,8029,779-1,00.html";
		const string administratorOptionsUrlRegEx = @"\<a .*href.*['""](<url>.*\.html['""].*\>Administrator Options\</a\>";
		const string familyListingRegEx = @"\<td width=""40%"" align=""left"" valign=""top"" class=""featurestext""\>\<font color=""""\>{0}\<\/font\>\<br\>\<\/td\>"
			+ @"\s+\<td width=""40%"" rowspan=""2"" align=""left"" valign=""top"" class=""featurestext""\>\<font color=""""\>"
			+ @"{2}\<br\>"
			+ @".+?\<a href=""([^""]+)"" class=""featureslink""\>change\<\/a\>";

		public delegate void UpdateStatusCallback(int percent);

		public static void Convert(IEnumerable<Family> families, Uri photosDirectory,
			string username, string password, UpdateStatusCallback update) {
			if (families == null) throw new ArgumentNullException("families");
			if (photosDirectory == null) throw new ArgumentNullException("photosDirectory");

			CookieCollection cookies = new CookieCollection();
			HtmlForm form = HtmlForm.GetForm(new Uri(loginUrl), cookies);
			form.Fields["username"] = username;
			form.Fields["password"] = password;
			form.Fields["Submit"] = "Submit";
			string adminDirectoryUrl;
			using (HttpWebResponse response = form.Submit()) {
				if (response.StatusCode != HttpStatusCode.Redirect || response.Headers["Location"].StartsWith("/units/login"))
					throw new ApplicationException("Login failed.");
				cookies.Add(response.Cookies);

				using (HttpWebResponse homePageResponse = HtmlClient.GetPage(new Uri(new Uri(loginUrl), response.Headers["Location"]), cookies)) {
					using (StreamReader sr = new StreamReader(homePageResponse.GetResponseStream())) {
						string homeWardContent = sr.ReadToEnd();
						Match m = Regex.Match(homeWardContent,
							@"\<a .*href.*['""](?<url>.*\.html)['""].*\>Administrator Options\</a\>");
						if (!m.Success) {
							throw new ApplicationException("Failed to locate Administrator Options web page.");
						}
						adminDirectoryUrl = new Uri(new Uri(loginUrl), m.Groups["url"].Value).AbsoluteUri;
					}
				}
			}
			int familyCount = 0;
			using (TempFileCollection tfc = new TempFileCollection()) {
				tfc.KeepFiles = false;
				for (char firstLetter = 'A'; firstLetter <= 'Z'; firstLetter++) {
					using (HttpWebResponse response = HtmlClient.GetPage(string.Format(adminDirectoryUrl, firstLetter), cookies)) {
						cookies.Add(response.Cookies);
						string pageContent;
						using (StreamReader sr = new StreamReader(response.GetResponseStream()))
							pageContent = sr.ReadToEnd();

						foreach (Family fam in GetFamiliesWithLastNamesStartingWith(firstLetter, families)) {
							if (update != null) update(familyCount++);
							string firstLineOfAddress = Regex.Match(fam.MailingAddress.Original, @"([^\n\r]+)").Groups[1].Value;
							string pattern = string.Format(familyListingRegEx, fam.Surname, fam.FamilyMembers[0].GivenNames, firstLineOfAddress);
							Match m = Regex.Match(pageContent, pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase);
							if (!m.Success) continue; // family not found.  skipping
							string changeUrl = m.Groups[1].Value;
							form = HtmlForm.GetForm(adminDirectoryUrl, changeUrl, cookies);
							// Delete existing picture if there is one.
							Match oldPictureMatch = Regex.Match(form.HtmlContent, @"\<a href=""(/units/a/admin/membership/imagedelete/.+?\.html)"" class=""featureslink""\>Change\</a\>");
							if (oldPictureMatch.Success)
								form = HtmlForm.GetForm(adminDirectoryUrl, oldPictureMatch.Groups[1].Value, cookies);
							// Set the initial fields
							Match familyNumberMatch = Regex.Match(form.HtmlContent, @"document\.form\.FamilyNumber\.value = '(\w+)';");
							if (!familyNumberMatch.Success) throw new ApplicationException("Could not find family number.");
							form.Fields["FamilyNumber"] = familyNumberMatch.Groups[1].Value;
							form.Fields["RawUploadImageName"] = string.Format("{0}, {1}.jpg", fam.Surname, fam.FamilyMembers[0].GivenNames);
							form.Fields["Submit"] = "Submit";
							// Set up for photo upload
							form.Files["UploadImage"] =
								fam.PreparePhoto(fam.GetPhotoPath(new Uri(photosDirectory.LocalPath)), tfc, MediaQuality.WardWebSite);
							// Submit changes
						}
					}
					using (HttpWebResponse response = form.Submit()) {
						if (response.StatusCode != HttpStatusCode.Found || !response.Headers["Location"].StartsWith("/units/a/admin/membership/"))
							throw new ApplicationException("Error submitting new photo.");
					}
				}
			}
		}

		static IEnumerable<Family> GetFamiliesWithLastNamesStartingWith(char firstLetter, IEnumerable<Family> families) {
			if (families == null) throw new ArgumentNullException("families");
			ICollection<Family> list = new List<Family>();
			foreach (Family f in families)
				if (f.Surname.StartsWith(firstLetter.ToString()))
					list.Add(f);
			return list;
		}
	}
}
